package com.zhny.iot.syb.weather.plugin;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.zhny.aiot.plugin.ICenterFacilities;
import com.zhny.aiot.plugin.IDeviceFactory;
import com.zhny.aiot.plugin.core.utils.MessageUtils;
import com.zhny.aiot.plugin.model.DeviceEntity;
import com.zhny.aiot.plugin.model.MessageEntry;
import com.zhny.aiot.plugin.model.MessageType;
import com.zhny.aiot.plugin.model.TaskEntity;
import com.zhny.aiot.plugin.web.WebDevice;
import com.zhny.iot.syb.weather.plugin.model.WeatherStoreEntry;
import com.zhny.iot.syb.weather.plugin.model.response.LoginToken;
import com.zhny.iot.syb.weather.plugin.model.response.Result;
import com.zhny.iot.syb.weather.plugin.model.response.WeatherDataResult;
import io.netty.util.internal.StringUtil;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.conn.ConnectTimeoutException;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.net.URISyntaxException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class WeatherDevice extends WebDevice<WeatherConfig> {
    private static final Logger log=LoggerFactory.getLogger (WeatherDevice.class);
    private String loginToken=null;
    private long loginTimeSpan=0L;

    public WeatherDevice(IDeviceFactory factory, ICenterFacilities centerFacilities, DeviceEntity entity) {
        super (factory, centerFacilities, entity);
    }

    @Override
    protected void onRequestHttp(TaskEntity task, CloseableHttpClient httpClient) {
        log.info ("execute syb weather api request!");
        if(this.loginToken == null || this.loginTimeSpan == 0)
            onLogin (httpClient);
        long currentTime=System.currentTimeMillis ();
        if(this.loginToken == null || this.loginTimeSpan == 0)
            return;
        if(currentTime-this.loginTimeSpan>1000 * 60 * 60 * 2) {
            onLogin (httpClient);
        }
        onEnvDataResponse (httpClient);
    }

    private void onLogin(CloseableHttpClient httpClient) {
        URIBuilder uriBuilder=null;
        WeatherConfig config=this.getConfig ();
        try {
            uriBuilder=new URIBuilder ("https://things.ysiot.net:18080/api/v1/login");
            ObjectMapper objectMapper = new ObjectMapper();
            Map<String, String> requestBody = new HashMap<>();
            requestBody.put("userName", config.getUsername());
            requestBody.put("password", config.getPassword());
            String jsonBody = objectMapper.writeValueAsString(requestBody);
            HttpPost httpPost=new HttpPost (uriBuilder.build ());
            httpPost.setEntity (new StringEntity (jsonBody,"UTF-8"));
            httpPost.setHeader("Content-Type", "application/json");
            try (CloseableHttpResponse response=httpClient.execute (httpPost)) {
                String body=EntityUtils.toString (response.getEntity ());
                Result<LoginToken> result=deserialize (body, new TypeReference<Result<LoginToken>> () {
                });
                if(result.getData () == null) {
                    log.error ("syb data token api result map to error !");
                    log.error ("{}", result.getMessage ());
                    return;
                }
                LoginToken token=result.getData ();
                if(token == null) {
                    log.error ("{}syb data login error ! return token is null !", this.getDeviceEntity ().getName ());
                    return;
                }
                String tk=token.getToken ();
                log.info ("token:{}", token);
                loginToken="Bearer "+tk;
                log.info ("authorization token:{}", loginToken);
                this.loginTimeSpan=System.currentTimeMillis ();

            } catch (ClientProtocolException e) {
                throw new RuntimeException (e);
            } catch (ConnectTimeoutException e) {
                throw new RuntimeException ("请求连接超时");
            } catch (IOException e) {
                throw new RuntimeException (e);
            }
        } catch (URISyntaxException e) {
            throw new RuntimeException (e);
        } catch (JsonProcessingException e) {
            throw new RuntimeException (e);
        }
    }

    private void onEnvDataResponse(CloseableHttpClient httpClient) {
        log.info ("read syb data api response!");
        URIBuilder uriBuilder=null;
        WeatherConfig config=this.getConfig ();
        try {
//            String httpUrl = "https://hnwlw.loongk.com/mobile/loadShackDatas/2c91b76773ba17db0173ec2915320032";
            String httpUrl="https://things.ysiot.net:18080/api/v1/dev/value?devCode="+config.getSn ();
            uriBuilder=new URIBuilder (httpUrl);
            HttpGet httpGet=new HttpGet (uriBuilder.build ());
            httpGet.addHeader ("Authorization", this.loginToken);
            try (CloseableHttpResponse response=httpClient.execute (httpGet)) {
                String body=EntityUtils.toString (response.getEntity ());
                Result<List<WeatherDataResult>> result=deserialize (body, new TypeReference<Result<List<WeatherDataResult>>> () {
                });
                if(result != null && result.isSuccess ()) {
                    if(result.getData ().isEmpty ())
                        return;
                    onResponse (result.getData ());
                }else {
                    log.error ("{}syb data response is null !", this.getDeviceEntity ().getName ());
                }

            } catch (ClientProtocolException e) {
                throw new RuntimeException (e);
            } catch (ConnectTimeoutException e) {
                throw new RuntimeException ("请求连接超时");
            } catch (IOException e) {
                throw new RuntimeException (e);
            }
        } catch (URISyntaxException e) {
            throw new RuntimeException (e);
        }
    }

    public void onResponse(List<WeatherDataResult> list) {
        MessageEntry messageEntry=MessageUtils.createMessageEntry (this.getId ());

        WeatherStoreEntry storeEntry=new WeatherStoreEntry ();
        for (WeatherDataResult result:list){
            if(result.getFieldName ().equals ("Temp"))
                storeEntry.setAirTemperature (String.valueOf (result.getValue ()));
            if(result.getFieldName ().equals ("Humi"))
                storeEntry.setHumidity (String.valueOf (result.getValue ()));
            if(result.getFieldName ().equals ("IlluKL"))
                storeEntry.setIllumination (String.valueOf (result.getValue ()));
            if(result.getFieldName ().equals ("BAP"))
                storeEntry.setAirPressure (String.valueOf (result.getValue ()));
            if(result.getFieldName ().equals ("WindS"))
                storeEntry.setSpeed (String.valueOf (result.getValue ()));
            if(result.getFieldName ().equals ("WindD360"))
                storeEntry.setDirection (String.valueOf (result.getValue ()));
            if(result.getFieldName ().equals ("RainV"))
                storeEntry.setRain (String.valueOf (result.getValue ()));
            if(result.getFieldName ().equals ("Radi"))
                storeEntry.setEt0 (String.valueOf (result.getValue ()));
            if(result.getFieldName ().equals ("SunDura"))
                storeEntry.setSunDuran (String.valueOf (result.getValue ()));
            if(result.getFieldName ().equals ("LDTemp"))
                storeEntry.setDewPointTemperature (String.valueOf (result.getValue ()));
            if(result.getFieldName ().equals ("ET0"))
                storeEntry.setEt0 (String.valueOf (result.getValue ()));
        }

        messageEntry.setType (MessageType.RESULT);
        messageEntry.setData (storeEntry);
        save (storeEntry);
        saveReal (storeEntry);

        nofity (messageEntry);
    }
    private <T> T deserialize(String json, Class<T> objectType) {
        if(StringUtil.isNullOrEmpty (json))
            return null;
        ObjectMapper mapper=new ObjectMapper ();
        try {
            mapper.configure (DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
            T obj=mapper.readValue (json, objectType);
            return obj;
//            return mapper.writeValueAsString(this);
        } catch (IOException e) {
            return null;
        }
    }

    public <T> T deserialize(String json, TypeReference<T> typeReference) {
        if(StringUtil.isNullOrEmpty (json))
            return null;
        ObjectMapper mapper=new ObjectMapper ();
        try {
            mapper.configure (DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
            T obj=mapper.readValue (json, typeReference);
            return obj;
//            return mapper.writeValueAsString(this);
        } catch (IOException e) {
            return null;
        }
    }
}
